// 创建一个进程任务池

#include <iostream>
#include <vector>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Task.hpp"

using namespace std;

int ppNum = 3; // 管道&进程数

class ProcTask
{
public:
  ProcTask(pid_t pid = pid_t(), int fd = int())
      : _child_pid(pid), _write_fd(fd)
  {
    // cout << "ProcTask() child_pid: " << _child_pid << "   write_fd: " << _write_fd << endl;
    char nameBuffer[64]; // 缓冲区
    snprintf(nameBuffer, sizeof nameBuffer, "Process %d [%d:%d]", num++, _child_pid, _write_fd);
  }

  ~ProcTask()
  {
    // cout << "~ProcTask() child_pid: " << _child_pid << "   write_fd: " << _write_fd << endl;
  }

  const string &name() const
  {
    return processName;
  }

  pid_t _child_pid;
  int _write_fd;
  string processName;
  static int num;
};

int ProcTask::num = 0;

void waitCommand()
{
  while (true)
  {
    cout << "子进程已就绪！" << endl;
    int command = 0;
    int n = read(0, &command, sizeof command);

    if (n == sizeof command)
      Task().Execute(command);
    else
    {
      cout << "子进程执行任务失败，已退出！ PID: " << getpid() << endl;
      break;
    }
  }
}

void creatPipeAndProc(vector<ProcTask> &PT)
{

  vector<int> fds;  //存储子进程中的写端信息

  for (int i = 0; i < ppNum; i++)
  {
    int pipefd[2] = {0};
    int ret = pipe(pipefd); // 创建管道
    assert(ret != -1);
    (void)ret;

    pid_t id = fork(); // 创建子进程
    assert(id != -1);
    (void)id;

    if (id == 0)
    {
      for(auto& e : fds)
        close(e);

      close(pipefd[1]); // 关闭写端

      dup2(pipefd[0], 0); // 与标准输入进行重定向

      waitCommand(); // 通过管道读取指令，并执行相应任务

      // 子进程退出
      close(pipefd[0]); // 关闭读端
      exit(0);
    }

    close(pipefd[0]); // 关闭读端

    PT.push_back(ProcTask(id, pipefd[1])); // 装载至进程池中

    fds.push_back(pipefd[1]);
  }
}

int showBoard()
{
  cout << "**************************" << endl;
  cout << "* 0.日志打印  1.数据插入 *" << endl;
  cout << "* 2.网络请求  3.退出程序 *" << endl;
  cout << "**************************" << endl;
  cout << "请选择:)";
  int task = 0;
  cin >> task;
  return task;
}

void ctrlProcess(vector<ProcTask> &PT)
{
  while (true)
  {
    // //2、下发任务（随机）
    // int task = rand() % taskNum;

    // 2、下发任务
    int task = showBoard();
    if (task == 3)
      break;
    if (task < 0 || task > 3)
    {
      cout << "选择错误，请重新选择!" << endl;
      continue;
    }

    // 3、指定进程（随机）
    int index = rand() % ppNum;
    cout << "当前选择进程：" << PT[index].name() << "   执行任务：" << task << endl;

    // 执行任务
    write(PT[index]._write_fd, &task, sizeof task);

    sleep(1);
  }
}

void waitChild(vector<ProcTask> &PT)
{
  //关闭写端，读端会被OS发出信号关闭
  for(int pos = 0; pos < ppNum; pos++)
  {
    close(PT[pos]._write_fd);
    waitpid(PT[pos]._child_pid, nullptr, 0);
  }
  cout << "所有子进程已被关闭" << endl;

  //sleep(10);

  // for(int pos = 0; pos < ppNum; pos++)
  //   waitpid(PT[pos]._child_pid, nullptr, 0);
  // cout << "所有子进程已被回收" << endl;
}

int main()
{
  vector<ProcTask> PT;

  // 1、创建五条管道及五个进程
  creatPipeAndProc(PT);
  sleep(1);

  // 2、进程控制
  ctrlProcess(PT);

  // 3、等待并回收子进程
  waitChild(PT);

  return 0;
}
